package org.ranki.moerae.clotho.context;

import org.ranki.moerae.clotho.node.Node;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

public class Context <T>{
    private T data;
    private Map<String,Object> params;
    private Map<Node, List<Node>> graph;
    private Set<Node> tabu;


    public Set<Node> getTabu() {
        return tabu;
    }

    public void setTabu(Set<Node> tabu) {
        this.tabu = tabu;
    }

    public Context() {
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public Context(T data) {
        this.data = data;
        this.graph=new HashMap<>();
        this.params=new HashMap<>();
        this.tabu=new HashSet<>();
    }

    public Map getParams() {
        return params;
    }

    public void setParams(Map params) {
        this.params = params;
    }

    public Map<Node, List<Node>> getGraph() {
        return graph;
    }

    public void setGraph(Map<Node, List<Node>> graph) {
        this.graph = graph;
    }
    private Object copy(Object source) throws Exception{

        Object o = null;
        Class clazz = source.getClass();
        o = clazz.newInstance();
        while(clazz != Object.class){

            Field fields[] = clazz.getDeclaredFields();
            for(Field field : fields) {
                if(Modifier.isStatic(field.getModifiers()))
                    continue;
                field.setAccessible(true);
                Object value = field.get(source);
                // 基本类型
                if(
                        "tabu".equals(field.getName())||
                        "params".equals(field.getName())||
                        "graph".equals(field.getName())||
                        field.getType().isPrimitive()
            ) {
                    field.set(o, value);
                }
                else if(value instanceof String){
                    field.set(o,new String((String) value));
                }
                // 数组类型  因为数组类型也是 Object 的实例, 所以写在前面
                else if(value!=null&&value.getClass().isArray() ) {
                    field.set(o, operateArray(value));
                }
                // 不为null的对象
                else if(value instanceof Object) {
                    field.set(o, copy(value));
                }
                field.setAccessible(false);
            }
            clazz = clazz.getSuperclass();
        }
        return o;
    }

    // 一维数组, 二维
    private Object operateArray(Object array) throws Exception{
        // 1. 数组不为null, 2. 数组长度 >= 0
        if(array == null)
            return null;

        int length = Array.getLength(array);
        Class componentType = array.getClass().getComponentType();

        // 基本类型 + String 类型, 因为String 类型的值是不变的, 所以采用等值
        if(componentType.isPrimitive() || componentType == String.class) {
            Object xxx = returnPrimitive(array, length);
            return xxx;
        }
        // 保证长度 > 0
        if(componentType.isArray()) {
            // 固定长度的多维数组
            Object value = Array.newInstance(array.getClass().getComponentType(), Array.getLength(array));
            int len = Array.getLength(array);
            for(int i = 0; i < len; i++) {
                Array.set(value, i, operateArray(Array.get(array, i)));
            }
            return value;
        }
        else {
            Object o = null;
            o = Array.newInstance(componentType, length);
            for(int i = 0; i < length; i++) {
                Object value = copy(Array.get(array, i));
                Array.set(o, i, value);
            }
            return o;
        }
    }

    private Object returnPrimitive(Object array, int length) {
        Class componentType = array.getClass().getComponentType();
        if(componentType == int.class)
            return Arrays.copyOf((int[])array, length);
        if(componentType == double.class)
            return Arrays.copyOf((double[])array, length);
        if(componentType == float.class)
            return Arrays.copyOf((float[])array, length);
        if(componentType == long.class)
            return Arrays.copyOf((int[])array, length);
        if(componentType == boolean.class)
            return Arrays.copyOf((boolean[])array, length);
        if(componentType == byte.class)
            return Arrays.copyOf((byte[])array, length);
        if(componentType == short.class)
            return Arrays.copyOf((short[])array, length);
        if(componentType == char.class)
            return Arrays.copyOf((char[])array, length);
        if(componentType == String.class)
            return Arrays.copyOf((String[])array, length);

        return null;
    }

    @Override
    public Context clone() {
        try {
            return (Context) copy(this);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
